Skip to content

Escape snapshot names in libvirt XML - #14201

Open
sureshanaparti wants to merge 1 commit into
apache:4.22from
shapeblue:escape-snapshot-names-libvirt
Open

sureshanaparti wants to merge 1 commit into
apache:4.22from
shapeblue:escape-snapshot-names-libvirt

Conversation

@sureshanaparti

Copy link
Copy Markdown
Contributor

Description

This PR escapes the snapshot names in the three places that build the document: LibvirtCreateVMSnapshotCommandWrapper, generateVMSnapshotXML (snapshot and parent), and the MessageFormat argument in LibvirtManageSnapshotCommandWrapper.
The raw name still goes to snapshotLookupByName, which takes a plain string, not markup.

Uses commons-lang StringEscapeUtils.escapeXml, same as LibvirtVMDef does for the VNC password, so no new dependency. Create and restore escape identically, so existing snapshots are still found by name.

The KVM wrappers build documents by string concatenation and drop the snapshot name straight in.
A name carrying XML metacharacters could close the element early and add arbitrary children, such as a block pointing anywhere on the hypervisor.

Nothing reaching these sinks today can carry a metacharacter: on KVM the vm.instancename.flag path is gated on VMware, hostnames go through NetUtils.verifyDomainNameLabel, and KVMStorageProcessor uses random UUIDs.
This is hardening so the sink no longer depends on its callers.

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial

Screenshots (if appropriate):

How Has This Been Tested?

How did you try to break this feature and the system with this change?

@sureshanaparti sureshanaparti added this to the 4.22.2 milestone Sep 18, 2026
@sureshanaparti

Copy link
Copy Markdown
Contributor Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@sureshanaparti a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress.

@codecov

codecov Bot commented Sep 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 17.98%. Comparing base (a8c8c18) to head (b8e990d).

Files with missing lines Patch % Lines
...wrapper/LibvirtCreateVMSnapshotCommandWrapper.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               4.22   #14201      +/-   ##
============================================
+ Coverage     17.97%   17.98%   +0.01%     
- Complexity    16184    16191       +7     
============================================
  Files          5930     5930              
  Lines        535615   535615              
  Branches      65582    65582              
============================================
+ Hits          96271    96341      +70     
+ Misses       428377   428301      -76     
- Partials      10967    10973       +6     
Flag Coverage Δ
uitests 4.02% <ø> (ø)
unittests 19.06% <75.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sureshanaparti sureshanaparti changed the title Escape snapshot names in libvirt XML to prevent XML injection Escape snapshot names in libvirt XML Sep 18, 2026
@sureshanaparti
sureshanaparti force-pushed the escape-snapshot-names-libvirt branch from 131ee43 to b8e990d Compare September 18, 2026 12:38
@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19284

@nvazquez nvazquez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM

@nvazquez

Copy link
Copy Markdown
Contributor

@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@nvazquez a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@JoaoJandre JoaoJandre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLGTM, did not test it

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-17026)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 52560 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr14201-t17026-kvm-ol8.zip
Smoke tests completed. 149 look OK, 0 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File

@kiranchavala kiranchavala left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before fix

  1. Take one memory snapshot of a running KVM VM
cmk create vmsnapshot virtualmachineid=<vm-uuid> snapshotmemory=true
cmk list vmsnapshots virtualmachineid=<vm-uuid> filter=id,name,type,state

  1. Check the snapshot state

virsh snapshot-list i-2-32-VM
 Name                          Creation Time               State
--------------------------------------------------------------------
 i-2-32-VM_VS_20260923045048   2026-09-23 04:50:49 +0000   running

  1. Check the database

Databse (select * from vm_snapshots)


*************************** 2. row ***************************
                 id: 2
               uuid: f6dd533b-94bb-4218-88dc-0d8ee5b2d4aa
               name: i-2-32-VM_VS_20260923045048
       display_name: test-snapshot
        description: test-snapshot
              vm_id: 32
         account_id: 2
          domain_id: 1
service_offering_id: 2
   vm_snapshot_type: DiskAndMemory
              state: Ready
             parent: NULL
            current: 1
       update_count: 2
            updated: 2026-09-23 04:50:56
            created: 2026-09-23 04:50:48
            removed: NULL
2 rows in set (0.00 sec)

  1. Update the database entry

mysql > UPDATE cloud.vm_snapshots SET name = CONCAT(name, '</name><description>INJECTED</description><name>x') WHERE uuid = 'f6dd533b-94bb-4218-88dc-0d8ee5b2d4aa';
  1. Stop the VM
cmk stop virtualmachine id=<vm-uuid>

  1. Start the VM. This sends RestoreVMSnapshotCommand to the host.

cmk start virtualmachine id=<vm-uuid>

  1. Check the KVM Agent log

[root@ref-trl-12298-k-Mr8-suresh-anaparti-kvm2 ~] grep -A8 "Restoring Instance Snapshot" /var/log/cloudstack/agent/agent.log | tail -20
2026-09-23 04:53:57,948 DEBUG [resource.wrapper.LibvirtRestoreVMSnapshotCommandWrapper] (AgentRequest-Handler-3:[]) (logid:e5d4f5ea) Restoring Instance Snapshot i-2-32-VM_VS_20260923045048</name><description>INJECTED</description><name>x on i-2-32-VM with XML:
 <domainsnapshot>
  <name>i-2-32-VM_VS_20260923045048</name><description>INJECTED</description><name>x</name>
  <state>running</state>
  <creationTime>1790139048</creationTime>
<domain type='kvm' id='18'>
  <name>i-2-32-VM</name>
  <uuid>9b044695-8d01-4265-84a5-667dec666ee5</uuid>
  <description>CentOS 5.5 (64-bit)</description>
  1. Check the xml
virsh snapshot-dumpxml i-2-32-VM i-2-32-VM_VS_20260923045048 | head

<domainsnapshot>
  <name>i-2-32-VM_VS_20260923045048</name>
  <description>INJECTED</description>
  <state>running</state>
  <creationTime>1790139048</creationTime>
  <memory snapshot='internal'/>
  <disks>
    <disk name='vda' snapshot='internal'/>
    <disk name='hdc' snapshot='no'/>
    <disk name='hdd' snapshot='no'/>


After fix

  1. Follow the same steps to create a snaphot

[root@ref-trl-12386-k-Mol8-kiran-chavala-kvm1 ~] virsh snapshot-list i-2-3-VM
 Name                         Creation Time               State
-------------------------------------------------------------------
 i-2-3-VM_VS_20260923050607   2026-09-23 05:06:08 +0000   running

[root@ref-trl-12386-k-Mol8-kiran-chavala-kvm1 ~]# virsh snapshot-dumpxml i-2-3-VM i-2-3-VM_VS_20260923050607 |head
<domainsnapshot>
  <name>i-2-3-VM_VS_20260923050607</name>
  <state>running</state>
  <creationTime>1790139968</creationTime>
  <memory snapshot='internal'/>
  <disks>
    <disk name='vda' snapshot='internal'/>
    <disk name='hdc' snapshot='no'/>
  </disks>
  <domain type='kvm'>

  1. Update the database
mysql> UPDATE cloud.vm_snapshots SET name = CONCAT(name, '</name><description>INJECTED</description><name>x') WHERE uuid ="66bfd611-f20b-446d-ae5f-a0938c90fe2f";
  1. stop the vm

  2. start the vm



[root@ref-trl-12386-k-Mol8-kiran-chavala-kvm1 ~]virsh snapshot-list i-2-3-VM
 Name   Creation Time   State
-------------------------------
[root@ref-trl-12386-k-Mol8-kiran-chavala-kvm1 ~]# virsh snapshot-dumpxml i-2-3-VM i-2-3-VM_VS_20260923050607 |head
error: Domain snapshot not found: no domain snapshot with matching name 'i-2-3-VM_VS_20260923050607'

Check the KVM Agent log and Management server log


[root@ref-trl-12386-k-Mol8-kiran-chavala-kvm1 ~]# grep -A8 "Restoring Instance Snapshot" /var/log/cloudstack/agent/agent.log | tail -20
2026-09-23 05:11:18,143 DEBUG [resource.wrapper.LibvirtRestoreVMSnapshotCommandWrapper] (AgentRequest-Handler-4:[]) (logid:47d1f8a0) Restoring Instance Snapshot i-2-3-VM_VS_20260923050607</name><description>INJECTED</description><name>x on i-2-3-VM with XML:
 <domainsnapshot>
  <name>i-2-3-VM_VS_20260923050607&lt;/name&gt;&lt;description&gt;INJECTED&lt;/description&gt;&lt;name&gt;x</name>
  <state>running</state>
  <creationTime>1790139968</creationTime>
<domain type='kvm' id='5'>
  <name>i-2-3-VM</name>
  <uuid>c74b883d-eee4-489d-8f64-82de065c2f61</uuid>
  <description>CentOS 5.5 (64-bit)</description>

2026-09-23 05:11:18,149 DEBUG [resource.wrapper.LibvirtRestoreVMSnapshotCommandWrapper] (AgentRequest-Handler-4:[]) (logid:47d1f8a0) Failed to restore Instance Snapshot i-2-3-VM_VS_20260923050607</name><description>INJECTED</description><name>x on i-2-3-VM




[root@ref-trl-12386-k-Mol8-kiran-chavala-mgmt1 ~]# 2026-09-23 05:11:18,197 WARN  [c.c.v.UserVmManagerImpl] (Work-Job-Executor-6:[ctx-3e55c107, job-44/job-45, ctx-6020f68b]) (logid:47d1f8a0) Unable to restore the Instance Snapshot from image file to the Instance: org.libvirt.LibvirtException: invalid snapshot name 'i-2-3-VM_VS_20260923050607</name><description>INJECTED</description><name>x': name can't contain '/'


Test case 2 ( revertVmsnapshot)

  1. Have a vm in running state

  2. Take a vm snapshot


[root@ref-trl-12386-k-Mol8-kiran-chavala-kvm1 ~]# virsh snapshot-dumpxml i-2-3-VM i-2-3-VM_VS_20260923050607 |head
<domainsnapshot>
  <name>i-2-3-VM_VS_20260923050607</name>
  <state>running</state>
  <creationTime>1790139968</creationTime>
  <memory snapshot='internal'/>
  <disks>
    <disk name='vda' snapshot='internal'/>
    <disk name='hdc' snapshot='no'/>
  </disks>
  <domain type='kvm'>

  1. Update the databse ( vm_snapshot)

mysql> UPDATE cloud.vm_snapshots SET name = CONCAT(name, '</name><description>INJECTED</description><name>x') WHERE uuid ="66bfd611-f20b-446d-ae5f-a0938c90fe2f";

  1. Revert Snapshot

Agent.log 

2026-09-23 05:40:16,765 WARN  [resource.wrapper.LibvirtRevertToVMSnapshotCommandWrapper] (AgentRequest-Handler-1:[]) (logid:57fe53eb)  Revert to Instance Snapshot failed due to org.libvirt.LibvirtException: Domain snapshot not found: no domain snapshot with matching name 'i-2-3-VM_VS_20260923050607</name><description>INJECTED</description><name>x' org.libvirt.LibvirtException: Domain snapshot not found: no domain snapshot with matching name 'i-2-3-VM_VS_20260923050607</name><description>INJECTED</description><name>x'
  at org.libvirt.ErrorHandler.processError(Unknown Source)
  at org.libvirt.ErrorHandler.processError(Unknown Source)
  at org.libvirt.Domain.snapshotLookupByName(Unknown Source)


Management server log 

2026-09-23 05:40:16,849 ERROR [c.c.a.ApiAsyncJobDispatcher] (API-Job-Executor-40:[ctx-c12b5551, job-55]) (logid:57fe53eb) Unexpected exception while executing org.apache.cloudstack.api.command.admin.vmsnapshot.RevertToVMSnapshotCmdByAdmin com.cloud.utils.exception.CloudRuntimeException: Unable to revert VM i-2-3-VM to snapshot i-2-3-VM_VS_20260923050607</name><description>INJECTED</description><name>x.
  at com.cloud.vm.snapshot.VMSnapshotManagerImpl.orchestrateRevertToVMSnapshot(VMSnapshotManagerImpl.java:988)
  at com.cloud.vm.snapshot.VMSnapshotManagerImpl.orchestrateRevertToVMSnapshot(VMSnapshotManagerImpl.java:1344)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
  at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.base/java.lang.reflect.Method.invoke(Method.java:569)


Image

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants